package com.lightjet.macross.base.gateway.config;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lightjet.macross.base.gateway.feign.BaseAuthRemoteClient;
import com.lightjet.macross.common.util.JwtUtil;
import com.lightjet.macross.common.util.RedisUtil;
import io.netty.util.internal.StringUtil;
import lombok.extern.java.Log;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;

@Log
@Component
public class AuthSignatureFilter implements GlobalFilter, Ordered {

    private static final String EXCHANGE_TOKEN_DEF = "exchange-token";
    private static final String TOKEN_SECRET_DEFAULT = "802sastcasc!";
    private static final String AUTH_TOKEN_PATH= "/base-auth/oauth/token";
    private static final String REDIS_KEY = "simpleauth:";
    private static final String CLAIM_USERNAME = "uid";
    private static final String CLAIM_PASSWORD = "pwd";
    private static final String CLAIM_CLEINTID = "cid";
    private static final String CLAIM_CLEINTSECRET = "cs";
    @Resource
    private RedisUtil redisUtil;

    @Resource
    private BaseAuthRemoteClient baseAuthRemoteClient;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        ServerHttpRequest request = exchange.getRequest();
        HttpHeaders headers = request.getHeaders();
        log.info(MessageFormat.format("接收到到客户端[{0}]请求：Uri->{1} ;Method->{2} ",request.getRemoteAddress(),request.getURI(),request.getMethod()));
        String convertedToken= "";
        String exchangeToken = headers.getFirst(EXCHANGE_TOKEN_DEF);
        if(request.getCookies().containsKey("Authorization") && !request.getMethod().equals(HttpMethod.OPTIONS))
        {
            // convertedToken = request.getCookies().get("Authorization").toString().replace("@"," ");
            log.info("检测到请求Cookie带有Authorization信息："+convertedToken);

        }
/*        else if(request.getQueryParams().containsKey("Authorization"))
        {
            convertedToken = request.getQueryParams().getFirst("Authorization").toString().replace("@"," ");
            //request.getQueryParams().put("Authorization", Collections.singletonList(""));
            log.info("检测到请求参数带有Authorization信息："+convertedToken);
        }*/
        else if (!StringUtil.isNullOrEmpty(exchangeToken)) //如果检测到有交换Token
        {
            log.info("检测到【exchange-token】:" + exchangeToken);
            ServerHttpResponse response = exchange.getResponse();

            //首先检查Cookie

            //首先校验Token 的有效性
            if (JwtUtil.verify(exchangeToken, TOKEN_SECRET_DEFAULT)) {
                log.info("验证【exchange-token】有效");
                HashMap<String, String> clientinfoMap = JwtUtil.getTokenClaims(exchangeToken, CLAIM_USERNAME, CLAIM_PASSWORD, CLAIM_CLEINTID, CLAIM_CLEINTSECRET);
                if (clientinfoMap.get(CLAIM_USERNAME) != null && clientinfoMap.get(CLAIM_CLEINTID) != null) {
                    String redisKey = REDIS_KEY + clientinfoMap.get(CLAIM_CLEINTID) + "-" + clientinfoMap.get(CLAIM_USERNAME);
                    String host = MessageFormat.format("{0}://{1}:{2}/",
                            request.getURI().getScheme(),
                            request.getURI().getHost(),
                            String.valueOf(request.getURI().getPort()));

                    log.info("Host->"+host+" 尝试从Redis获取key为【" + redisKey + "】的Token");
                    //首先从缓存中取出 真正的 OAUTH2 的Token
                    if (redisUtil.hasKey(redisKey)) {
                        String[] tokenAndExp = redisUtil.get(redisKey).toString().split("@");
                        //如果存在redis里的 Token 过期时间存在 （token@exptime ）
                        if (tokenAndExp.length > 1) {
                            //如果该token 已过期
                            if ((new Date()).getTime() > Long.parseLong(tokenAndExp[1])) {
                                log.info("从Redis获取的Token已过期");
                                //重新获取OAUTH Token 并保存Redis
                                convertedToken = converToken(clientinfoMap, host);
                            } else//若未过期 则直接取该Token
                            {

                                convertedToken = tokenAndExp[0];
                                log.info("从Redis获取的Token【" + convertedToken + "】未过期：");
                            }
                        } else {
                            //重新获取OAUTH Token 并保存Redis
                            convertedToken = converToken(clientinfoMap, host);
                        }
                    } else {
                        //重新获取OAUTH Token 并保存Redis
                        convertedToken = converToken(clientinfoMap, host);
                    }
                } else {
                    response.setStatusCode(HttpStatus.UNAUTHORIZED);
                    return response.setComplete();
                }


            } else {
                log.info("验证【exchange-token】无效!");
/*                response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();*/
            }




        /*try {
            log.info("request:"+ new ObjectMapper().writeValueAsString(exchange));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }*/
        }
        if (!StringUtil.isNullOrEmpty(convertedToken)) {
            log.info("已将token添加至header【" + HttpHeaders.AUTHORIZATION + "】");
            request = request.mutate().header(HttpHeaders.AUTHORIZATION, convertedToken).build();
            //headers.add(HttpHeaders.AUTHORIZATION,convertedToken);
            return chain.filter(exchange.mutate().request(request).build());
        }
        else
        {
            return chain.filter(exchange);
        }

    }

    private String converToken(HashMap<String,String> exchangeTokenMap,String host)
    {
        log.info("重新从Auth服务进行密码认证");
        String token = "";
        try {
            HashMap resp = null;
            String username = "*";
            if(StringUtil.isNullOrEmpty(exchangeTokenMap.get(CLAIM_USERNAME))) // exchangeToken 无 用户ID 则走 客户端认证模式
            {
                log.info("正在进行客户端认证模式：" + exchangeTokenMap.get(CLAIM_CLEINTID));
                resp = baseAuthRemoteClient.GetToken(
                        exchangeTokenMap.get(CLAIM_CLEINTID),
                        exchangeTokenMap.get(CLAIM_CLEINTSECRET),
                        "","",
                        "all", "client_credentials");
            }
            else
            {
                log.info("正在进行密码认证模式：" + exchangeTokenMap.get(CLAIM_CLEINTID));
                resp = baseAuthRemoteClient.GetToken( // exchangeToken 有 用户ID 则走 密码认证模式
                        exchangeTokenMap.get(CLAIM_CLEINTID),
                        exchangeTokenMap.get(CLAIM_CLEINTSECRET),
                        exchangeTokenMap.get(CLAIM_USERNAME),
                        exchangeTokenMap.get(CLAIM_PASSWORD),
                        "all", "password");
                username = exchangeTokenMap.get(CLAIM_USERNAME);
            }

        /*MultiValueMap<String ,String> parms = new LinkedMultiValueMap<>();

        parms.add("password",);
        parms.add("username",);
        parms.add("client_id",);
        parms.add("client_secret",);
        parms.add("scope","all");
        parms.add("grant_type","password");
        HashMap resp =  new RestTemplate().postForObject(host+AUTH_TOKEN_PATH,parms, HashMap.class);*/

            token = resp.get("token_type").toString() + " " + resp.get("access_token").toString();
            log.info("密码认证成功：" + token);
            long expirein = Long.parseLong(resp.get("expires_in").toString());
            redisUtil.set(REDIS_KEY + exchangeTokenMap.get(CLAIM_CLEINTID) + "-" + username, token + "@" + (expirein * 1000 + new Date().getTime()));
        }
        catch (Exception e)
        {
            log.info("从Auth服务进行密码认证失败："+e.getMessage());
        }
        return token;
    }

    @Override
    public int getOrder() {
        return 0;
    }


}
